#pragma once
#ifndef __LIT__ITEM__MANAGER__HH__
#define __LIT__ITEM__MANAGER__HH__

#include <OgreLogManager.h>
#include "LitString.h"
#include "LitExceptions.h"
#include "LitPrerequisites.h"

namespace Lit
{
	
	class  LIT_API Item
	{
		bool		mIsCreated;
	protected:
		String		mName;

	public:
		Item( const String &name="" ) 
			: mName( name )
			, mIsCreated( false )
		{
		}
		virtual ~Item( ){}
		
		void _setCreated( bool state ){ mIsCreated = state; }
		bool isCreated() const{ return mIsCreated; }

		const String& getName() const{ return mName; }
		void setName( const String &name ){ mName = name; }

		virtual void create() = 0;
		virtual void destroy() = 0;
	};

	template<typename T>
	class LIT_API ItemManager
	{
	public:
		typename typedef T item_t;
		typename typedef T* item_ptr_t;
		typename typedef std::shared_ptr<item_t>	ptr_t;
		typename typedef std::weak_ptr<item_t>		ref_t;
		typename typedef std::vector<ptr_t>			list_t;
		typename typedef list_t::iterator			itor_t;
		typename typedef list_t::const_iterator		const_itor_t;
		
		list_t v;

		String mName;
	public:
		ItemManager( const String &name ) : mName( name ){}
		virtual ~ItemManager( ){ clear(); }

		virtual void itemCreated( ptr_t p )=0;		
		virtual void itemDestroyed( const String &name )=0;


		const String& getName() const{ return mName; }

		itor_t begin(){ return v.begin(); }
		const_itor_t begin() const{ return v.begin(); }
		itor_t end(){ return v.end(); }
		const_itor_t end() const{ return v.end(); }

		size_t count() const{ return v.size(); }
		bool empty() const{ return v.empty(); }

		const ref_t& at( size_t id ) const{ return v.at(id); }
		bool has( const String &name ) const
		{
			for( auto i=v.begin(); i!=v.end(); i++ )
				if( !(*i) && !(*i)->getName().empty() && (*i)->getName() == name )
					return true;
			return false;
		}
		ref_t push( Item* p, bool createNow = true )
		{
			if( !p ) throw std::runtime_error("cannot push an invalid object");
			if( has(p->getName()) ) throw ItemDuplicate(p->getName(), EXC_LOC );
			ptr_t p2((T*)p);
			v.push_back( p2 );
			if( createNow ){
				if( !v.back()->isCreated() ){
					itemCreated(p2);
					v.back()->create();
				}
			}
			return p2;
		}
		const_itor_t find( const String &name ) const
		{
			for( auto i=v.cbegin(); i!=v.cend(); i++ )
				if( (*i)->getName() == name )
					return i;
			return v.cend();
		}
		itor_t find( const String &name )
		{
			for( auto i=v.begin(); i!=v.end(); i++ )
				if( (*i)->getName() == name )
					return i;
			return v.end();
		}
		ref_t get( const String &name )
		{
			if( !has( name ) )
				throw ItemMissing( name, EXC_LOC );
			return *find( name );
		}
		void clear(){ 
			for( auto i=v.begin(); i!=v.end(); i++ ) 
			{
				itemDestroyed( (*i)->getName() );
				if( (*i)->isCreated() ) (*i)->destroy();
			}
			v.clear(); 
		}
		void pop( const String &name )
		{
			if( !has( name ) )
				throw ItemMissing( name, EXC_LOC );
			itor_t i = find(name);
			_destroy( i );
		}
		void _destroy( itor_t i, bool remove =true )
		{
			if( i==v.end() ) return;
			itemDestroyed( (*i)->getName() );
			if( (*i)->isCreated() ) (*i)->destroy();
			if( remove ) v.erase( i );
		}
	};
};

#endif